﻿using System.Collections.Generic;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Domain.Repositories;
using pandx.Mulan.Dictionaries.Dto;
using Abp.Linq.Extensions;
using System.Linq;
using Abp.Authorization;
using Abp.Extensions;
using Microsoft.EntityFrameworkCore;
using pandx.Mulan.Authorization;
using pandx.Mulan.Authorization.Users;
using pandx.Mulan.Dto;
using pandx.Mulan.Dictionaries.Exporting;

namespace pandx.Mulan.Dictionaries
{
    public class DictionaryAppService:MulanAppServiceBase,IDictionaryAppService
    {
        private readonly IRepository<Dictionary, long> _dictionaryRepository;
        private readonly IRepository<User, long> _userRepository;
        private readonly IDictionaryListExcelExporter _dictionaryListExcelExporter;
        private readonly IRepository<DictionaryCategory, long> _dictionaryCategoryRepository;

        public DictionaryAppService(
            IRepository<Dictionary, long> dictionaryRepository,
            IRepository<User,long> userRepository,
            IDictionaryListExcelExporter dictionaryListExcelExporter,
            IRepository<DictionaryCategory,long> dictionaryCategoryRepository)
        {
            _userRepository = userRepository;
            _dictionaryRepository = dictionaryRepository;
            _dictionaryListExcelExporter = dictionaryListExcelExporter;
            _dictionaryCategoryRepository = dictionaryCategoryRepository;
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Browse)]
        public async Task<PagedResultDto<DictionaryListDto>> ObtainDictionaries(ObtainDictionariesInput input)
        {

            var query = CreateDictionaryAndUsersQuery(input);

            var dictionariesCount = await query.CountAsync();
            var dictionaries = await query.OrderBy(input.Sorting).PageBy(input).ToListAsync();
            var dtos = dictionaries.Select(item =>
            {
                var dto = ObjectMapper.Map<DictionaryListDto>(item.Dictionary);
                dto.CreatorUserName = item.User.UserName;
                dto.CreatorName = item.User.Name;
                dto.CategoryName = item.DictionaryCategory.Name;
                return dto;
            }).ToList();
            return new PagedResultDto<DictionaryListDto>(dictionariesCount, dtos);
        }
        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Browse)]
        public async Task<ListResultDto<DictionaryListDto>> GetDictionariesByCategoryCode(GetDictionariesByCategoryCodeInput input)
        {
            var query = from dictionary in _dictionaryRepository.GetAll()
                join category in _dictionaryCategoryRepository.GetAll() on dictionary.CategoryId equals category.Id
                where category.Code == input.Code && dictionary.Status
                select dictionary;
            return new ListResultDto<DictionaryListDto>(
                ObjectMapper.Map<List<DictionaryListDto>>(await query.ToListAsync()));
        }

        public async Task CreateOrUpdateDictionary(CreateOrUpdateDictionaryInput input)
        {
            if (input.Dictionary.Id.HasValue)
            {
                await UpdateDictionaryAsync(input);
            }
            else
            {
                await CreateDictionaryAsync(input);
            }
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Update)]
        protected virtual async Task UpdateDictionaryAsync(CreateOrUpdateDictionaryInput input)
        {
            var dictionary = await _dictionaryRepository.GetAsync(input.Dictionary.Id.Value);
            ObjectMapper.Map(input.Dictionary, dictionary);
            await CurrentUnitOfWork.SaveChangesAsync();
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Create)]
        protected virtual async Task CreateDictionaryAsync(CreateOrUpdateDictionaryInput input)
        {
            if (AbpSession.TenantId.HasValue)
            {
            }
            var dictionary = ObjectMapper.Map<Dictionary>(input.Dictionary);

            dictionary.TenantId=AbpSession.TenantId;

            await _dictionaryRepository.InsertAsync(dictionary);
            await CurrentUnitOfWork.SaveChangesAsync();
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Create,AppPermissions.Pages_Administration_Dictionaries_Update)]
        public async Task<GetDictionaryForEditOutput> GetDictionaryForEdit(NullableIdDto<long> input)
        {
            var output = new GetDictionaryForEditOutput();
            if (input.Id.HasValue)
            {
                var dictionary = await _dictionaryRepository.GetAsync(input.Id.Value);
                output.Dictionary = ObjectMapper.Map<DictionaryEditDto>(dictionary);
            }
            else
            {
                var dictionary = new DictionaryEditDto();
                output.Dictionary = dictionary;
            }

            return output;
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Browse)]
        public async Task<DictionaryListDto> GetDictionary(EntityDto<long> input)
        {
            var dictionary = await _dictionaryRepository.GetAsync(input.Id);
            return ObjectMapper.Map<DictionaryListDto>(dictionary);
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Delete)]
        public async Task DeleteDictionary(EntityDto<long> input)
        {
            var dictionary = await _dictionaryRepository.GetAsync(input.Id);
            await _dictionaryRepository.DeleteAsync(input.Id);
        }

        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Export)]
        public async Task<FileDto> ObtainDictionariesToExcel(ObtainDictionariesInput input)
        {
            var dictionaries = await CreateDictionaryAndUsersQuery(input).AsNoTracking().OrderBy(input.Sorting).ToListAsync();
            var dtos=dictionaries.Select(item =>
            {
                var dto = ObjectMapper.Map<DictionaryListDto>(item.Dictionary);
                dto.CreatorUserName = item.User.UserName;
                dto.CreatorName = item.User.Name;
                dto.CategoryName = item.DictionaryCategory.Name;
                return dto;
            }).ToList();
            return _dictionaryListExcelExporter.ExportToExcel(dtos);
        }
        
        private IQueryable<DictionaryAndUser> CreateDictionaryAndUsersQuery(ObtainDictionariesInput input)
        {
            if (input.Status == null || input.Status?.Count == 0)
            {
                input.Status = new List<bool> { true, false };
            }
            var query = from dictionary in _dictionaryRepository.GetAll()   
                join category in _dictionaryCategoryRepository.GetAll() on dictionary.CategoryId equals category.Id
                join user in _userRepository.GetAll() on dictionary.CreatorUserId equals user.Id into userJoin
                from joinedUser in userJoin.DefaultIfEmpty()
                where dictionary.CreationTime >= input.StartTime && dictionary.CreationTime <= input.EndTime
                select new DictionaryAndUser
                {
                    DictionaryCategory =category,
                    Dictionary = dictionary,
                    User=joinedUser
                };
            query = query
                .Where(item=>input.Status.Contains(item.Dictionary.Status))
                .WhereIf(!input.Filter.IsNullOrWhiteSpace(),item=>item.User.Name.Contains(input.Filter) || item.User.UserName.Contains(input.Filter) || item.Dictionary.Name.Contains(input.Filter) || item.Dictionary.Description.Contains(input.Filter) || item.DictionaryCategory.Name.Contains(input.Filter))
                .WhereIf(input.CategoryId != 0, item => item.Dictionary.CategoryId == input.CategoryId);
            return query;
        }
        
        // public async Task<ValidateOutput> ValidateValue(ValidateInput<string, long> input)
        // {
        //     if (input.Id == 0)
        //     {
        //         var one = await _dictionaryRepository.GetAll()
        //             .SingleOrDefaultAsync(r => r.Value == input.Value && r.CategoryId == input.ParentId);
        //         if (one != null)
        //         {
        //             return new ValidateOutput
        //             {
        //                 Status = false,
        //                 Message = "相同字典分类下已存在值为 " + input.Value + " 的字典条目"
        //             };
        //         }
        //         else
        //         {
        //             return new ValidateOutput
        //             {
        //                 Status = true,
        //             };
        //         }
        //     }
        //     else
        //     {
        //         var one = await _dictionaryRepository.GetAll().SingleOrDefaultAsync(r =>
        //             r.Value == input.Value && r.Id != input.Id && r.CategoryId == input.ParentId);
        //         if (one != null)
        //         {
        //             return new ValidateOutput
        //             {
        //                 Status = false,
        //                 Message = "相同字典分类下已存在值为 " + input.Value + " 的字典条目"
        //             };
        //         }
        //         else
        //         {
        //             return new ValidateOutput
        //             {
        //                 Status = true,
        //             };
        //         }
        //     }
        // }
        [AbpAuthorize(AppPermissions.Pages_Administration_Dictionaries_Create,AppPermissions.Pages_Administration_Dictionaries_Update)]
        public async Task<ValidateOutput> ValidateValue(ValidateInput<string, long> input)
        {
            if (input.Id == 0)
            {
                var one = await _dictionaryRepository.GetAll().SingleOrDefaultAsync(r => r.Value == input.Value);
                if (one != null)
                {
                    return new ValidateOutput
                    {
                        Status = false,
                        Message = "字典条目值 " + input.Value + " 已被占用"
                    };
                }
                else
                {
                    return new ValidateOutput
                    {
                        Status = true,
                    };
                }
            }
            else
            {
                var one = await _dictionaryRepository.GetAll().SingleOrDefaultAsync(r =>
                    r.Value == input.Value && r.Id != input.Id);
                if (one != null)
                {
                    return new ValidateOutput
                    {
                        Status = false,
                        Message = "字典条目值 " + input.Value + " 已被占用"
                    };
                }
                else
                {
                    return new ValidateOutput
                    {
                        Status = true,
                    };
                }
            }
        }
    }
}
